/*
 * Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
/*
 * Licensed Materials - Property of IBM
 * RMI-IIOP v1.0
 * Copyright IBM Corp. 1998 1999  All Rights Reserved
 *
 */

package com.sun.corba.se.impl.corba;


import org.omg.CORBA.Any;
import org.omg.CORBA.ARG_IN;
import org.omg.CORBA.ARG_OUT;
import org.omg.CORBA.ARG_INOUT;
import org.omg.CORBA.Context;
import org.omg.CORBA.ContextList;
import org.omg.CORBA.Environment;
import org.omg.CORBA.ExceptionList;
import org.omg.CORBA.NVList;
import org.omg.CORBA.NamedValue;
import org.omg.CORBA.Request;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TypeCodePackage.BadKind;
import org.omg.CORBA.UnknownUserException;
import org.omg.CORBA.Bounds;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.WrongTransaction;

import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.RemarshalException;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.OutputStream;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.presentation.rmi.StubAdapter;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.corba.AsynchInvoke;

public class RequestImpl
    extends Request {
  ///////////////////////////////////////////////////////////////////////////
  // data members

  protected org.omg.CORBA.Object _target;
  protected String _opName;
  protected NVList _arguments;
  protected ExceptionList _exceptions;
  private NamedValue _result;
  protected Environment _env;
  private Context _ctx;
  private ContextList _ctxList;
  protected ORB _orb;
  private ORBUtilSystemException _wrapper;

  // invocation-specific stuff
  protected boolean _isOneWay = false;
  private int[] _paramCodes;
  private long[] _paramLongs;
  private java.lang.Object[] _paramObjects;

  // support for deferred invocations.
  // protected instead of private since it needs to be set by the
  // thread object doing the asynchronous invocation.
  protected boolean gotResponse = false;

  ///////////////////////////////////////////////////////////////////////////
  // constructor

  // REVISIT - used to be protected.  Now public so it can be
  // accessed from xgiop.
  public RequestImpl(ORB orb,
      org.omg.CORBA.Object targetObject,
      Context ctx,
      String operationName,
      NVList argumentList,
      NamedValue resultContainer,
      ExceptionList exceptionList,
      ContextList ctxList) {

    // initialize the orb
    _orb = orb;
    _wrapper = ORBUtilSystemException.get(orb,
        CORBALogDomains.OA_INVOCATION);

    // initialize target, context and operation name
    _target = targetObject;
    _ctx = ctx;
    _opName = operationName;

    // initialize argument list if not passed in
    if (argumentList == null) {
      _arguments = new NVListImpl(_orb);
    } else {
      _arguments = argumentList;
    }

    // set result container.
    _result = resultContainer;

    // initialize exception list if not passed in
    if (exceptionList == null) {
      _exceptions = new ExceptionListImpl();
    } else {
      _exceptions = exceptionList;
    }

    // initialize context list if not passed in
    if (ctxList == null) {
      _ctxList = new ContextListImpl(_orb);
    } else {
      _ctxList = ctxList;
    }

    // initialize environment
    _env = new EnvironmentImpl();

  }

  public org.omg.CORBA.Object target() {
    return _target;
  }

  public String operation() {
    return _opName;
  }

  public NVList arguments() {
    return _arguments;
  }

  public NamedValue result() {
    return _result;
  }

  public Environment env() {
    return _env;
  }

  public ExceptionList exceptions() {
    return _exceptions;
  }

  public ContextList contexts() {
    return _ctxList;
  }

  public synchronized Context ctx() {
    if (_ctx == null) {
      _ctx = new ContextImpl(_orb);
    }
    return _ctx;
  }

  public synchronized void ctx(Context newCtx) {
    _ctx = newCtx;
  }

  public synchronized Any add_in_arg() {
    return _arguments.add(org.omg.CORBA.ARG_IN.value).value();
  }

  public synchronized Any add_named_in_arg(String name) {
    return _arguments.add_item(name, org.omg.CORBA.ARG_IN.value).value();
  }

  public synchronized Any add_inout_arg() {
    return _arguments.add(org.omg.CORBA.ARG_INOUT.value).value();
  }

  public synchronized Any add_named_inout_arg(String name) {
    return _arguments.add_item(name, org.omg.CORBA.ARG_INOUT.value).value();
  }

  public synchronized Any add_out_arg() {
    return _arguments.add(org.omg.CORBA.ARG_OUT.value).value();
  }

  public synchronized Any add_named_out_arg(String name) {
    return _arguments.add_item(name, org.omg.CORBA.ARG_OUT.value).value();
  }

  public synchronized void set_return_type(TypeCode tc) {
    if (_result == null) {
      _result = new NamedValueImpl(_orb);
    }
    _result.value().type(tc);
  }

  public synchronized Any return_value() {
    if (_result == null) {
      _result = new NamedValueImpl(_orb);
    }
    return _result.value();
  }

  public synchronized void add_exception(TypeCode exceptionType) {
    _exceptions.add(exceptionType);
  }

  public synchronized void invoke() {
    doInvocation();
  }

  public synchronized void send_oneway() {
    _isOneWay = true;
    doInvocation();
  }

  public synchronized void send_deferred() {
    AsynchInvoke invokeObject = new AsynchInvoke(_orb, this, false);
    new Thread(invokeObject).start();
  }

  public synchronized boolean poll_response() {
    // this method has to be synchronized even though it seems
    // "readonly" since the thread object doing the asynchronous
    // invocation can potentially update this variable in parallel.
    // updates are currently simply synchronized againt the request
    // object.
    return gotResponse;
  }

  public synchronized void get_response()
      throws org.omg.CORBA.WrongTransaction {
    while (gotResponse == false) {
      // release the lock. wait to be notified by the thread that is
      // doing the asynchronous invocation.
      try {
        wait();
      } catch (InterruptedException e) {
      }
    }
  }

  ///////////////////////////////////////////////////////////////////////////
  // private helper methods

  /*
   * The doInvocation operation is where the real mechanics of
   * performing the request invocation is done.
   */
  protected void doInvocation() {
    org.omg.CORBA.portable.Delegate delegate = StubAdapter.getDelegate(
        _target);

    // Initiate Client Portable Interceptors.  Inform the PIHandler that
    // this is a DII request so that it knows to ignore the second
    // inevitable call to initiateClientPIRequest in createRequest.
    // Also, save the RequestImpl object for later use.
    _orb.getPIHandler().initiateClientPIRequest(true);
    _orb.getPIHandler().setClientPIInfo(this);

    InputStream $in = null;
    try {
      OutputStream $out = delegate.request(null, _opName, !_isOneWay);
      // Marshal args
      try {
        for (int i = 0; i < _arguments.count(); i++) {
          NamedValue nv = _arguments.item(i);
          switch (nv.flags()) {
            case ARG_IN.value:
              nv.value().write_value($out);
              break;
            case ARG_OUT.value:
              break;
            case ARG_INOUT.value:
              nv.value().write_value($out);
              break;
          }
        }
      } catch (org.omg.CORBA.Bounds ex) {
        throw _wrapper.boundsErrorInDiiRequest(ex);
      }

      $in = delegate.invoke(null, $out);
    } catch (ApplicationException e) {
      // REVISIT - minor code.
      // This is already handled in subcontract.
      // REVISIT - uncomment.
      //throw new INTERNAL();
    } catch (RemarshalException e) {
      doInvocation();
    } catch (SystemException ex) {
      _env.exception(ex);
      // NOTE: The exception should not be thrown.
      // However, JDK 1.4 and earlier threw the exception,
      // so we keep the behavior to be compatible.
      throw ex;
    } finally {
      delegate.releaseReply(null, $in);
    }
  }

  // REVISIT -  make protected after development - so xgiop can get it.
  public void unmarshalReply(InputStream is) {
    // First unmarshal the return value if it is not void
    if (_result != null) {
      Any returnAny = _result.value();
      TypeCode returnType = returnAny.type();
      if (returnType.kind().value() != TCKind._tk_void) {
        returnAny.read_value(is, returnType);
      }
    }

    // Now unmarshal the out/inout args
    try {
      for (int i = 0; i < _arguments.count(); i++) {
        NamedValue nv = _arguments.item(i);
        switch (nv.flags()) {
          case ARG_IN.value:
            break;
          case ARG_OUT.value:
          case ARG_INOUT.value:
            Any any = nv.value();
            any.read_value(is, any.type());
            break;
        }
      }
    } catch (org.omg.CORBA.Bounds ex) {
      // Cannot happen since we only iterate till _arguments.count()
    }
  }
}
